// Package jwt 处理 JWT 认证
package jwt

import (
	"errors"
	"time"

	"gitee.com/sweetsunnyflower/carpooling-server-go/pkg/app"
	"gitee.com/sweetsunnyflower/carpooling-server-go/pkg/config"
	"gitee.com/sweetsunnyflower/carpooling-server-go/pkg/logger"
	"github.com/golang-jwt/jwt/v5"
)

var (
	ErrTokenExpired           error = errors.New("令牌已过期")
	ErrTokenExpiredMaxRefresh error = errors.New("令牌已过最大刷新时间")
	ErrTokenMalformed         error = errors.New("请求令牌格式有误")
	ErrTokenInvalid           error = errors.New("请求令牌无效")
	ErrHeaderEmpty            error = errors.New("需要认证才能访问！")
	ErrHeaderMalformed        error = errors.New("请求头中 Authorization 格式有误")
)

// JWT 定义一个jwt对象
type JWT struct {

	// 秘钥，用以加密 JWT，读取配置信息 app.key
	SignKey []byte

	// 刷新 Token 的最大过期时间
	// MaxRefresh time.Duration
	*JWT
}

func New() *JWT {
	return &JWT{
		SignKey: []byte(config.GetString("app.key")),
	}
}

func (j *JWT) CreateToken(userId string, openId string, tokenType string) (token string, err error) {
	// Create the Claims
	claims := jwt.MapClaims{
		"user_id": userId,
		"open_id": openId,
		"type":    tokenType,
		"exp":     j.expireAtTime(),
	}

	return j.createToken(claims)
}

// createToken 创建 Token，内部使用，外部请调用 IssueToken
func (j *JWT) createToken(claims jwt.Claims) (string, error) {
	// 使用HS256算法进行token生成
	// Create token
	pack := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Generate encoded token and send it as response.
	t, err := pack.SignedString(j.SignKey)
	logger.LogIf(err)
	if err != nil {
		return t, err
	}

	return t, nil
}

// expireAtTime 过期时间
func (j *JWT) expireAtTime() int64 {
	timenow := app.TimenowInTimezone()

	var expireTime int64
	if config.GetBool("app.debug") {
		expireTime = config.GetInt64("jwt.debug_expire_time")
	} else {
		expireTime = config.GetInt64("jwt.expire_time")
	}

	expire := time.Duration(expireTime) * time.Minute
	return timenow.Add(expire).Unix()
}

// parseTokenString 使用 jwtpkg.ParseWithClaims 解析 Token
func (j *JWT) ParseTokenString(tokenString string) (string, error) {
	token, err := jwt.ParseWithClaims(tokenString, &jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SignKey, nil
	})

	logger.LogIf(err)
	claims, _ := token.Claims.(jwt.MapClaims)
	// 解析成功后，可以通过 token.Claims 获取其中的数据
	if token.Valid {

		if user_id, ok := claims["user_id"]; ok {
			return user_id.(string), nil
		}

		return "", errors.New("解析token失败")

	}

	return "", errors.New("解析token失败")
}
